home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 January / macformat-020.iso / Shareware City / Developers / apps.to.go / DTS.Lib / CIconControl.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-07-14  |  15.2 KB  |  596 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** Program:         cciconcontrol.c
  5. ** Written by:      Eric Soldan
  6. **
  7. ** Copyright © 1993 Apple Computer, Inc.
  8. ** All rights reserved.
  9. */
  10.  
  11. /* You may incorporate this sample code into your applications without
  12. ** restriction, though the sample code has been provided "AS IS" and the
  13. ** responsibility for its operation is 100% yours.  However, what you are
  14. ** not permitted to do is to redistribute the source as "DSC Sample Code"
  15. ** after having made changes. If you're going to re-distribute the source,
  16. ** we require that you make it clear in the source that the code was
  17. ** descended from Apple Sample Code, but that you've made changes. */
  18.  
  19. /* CIcon control theory of operation:
  20. **
  21. ** The CIcon control stores the id passed to it in the refCon field of the control.
  22. ** When the defProc is called, it draws the 'cicn' smartly using the GWLayers CIcon
  23. ** utilities.  (These utilities can draw the 'cicn', even if PlotCIcon isn't available.)
  24. **
  25. ** When the CIcon control is hit, it draws that 'cicn' number plus 1.  So, if your id
  26. ** for the control is 1000, 'cicn' #1000 is the up-state of the button, and #1001 is
  27. ** the down state.  It doesn't just invert the base 'cicn', as that doesn't allow for
  28. ** some effects, such as buttons that push in when clicked on.
  29. **
  30. ** If there is no base+1 'cicn', then the base 'cicn' is inverted for hilite==1 state.
  31. **
  32. ** For hilite==255, if there is a base+2 'cicn', it is drawn, otherwise the base 'cicn' is used.
  33. **
  34. ** base, base+1, and base+2 are slight simplifications.  The actual case is base, base+n, base+n+n,
  35. ** where n is the number of 'cicn's necessary to tile the control rect.
  36. ** 'cicn' #base is upper-left, #base+1 is just to the right, etc.
  37. **
  38. ** A variant usage of the CIcon control is a family of buttons.  Instead of tracking
  39. ** the single CIcon control, and having it revert back to the unclicked state after
  40. ** release, this variation is used for buttons that push in and stick.
  41. **
  42. ** The below rules apply for variant #1 of the CIcon control:
  43. **
  44. ** • The user clicks on a CIcon control.  As usual, the hilite==1 state is displayed.
  45. ** • In addition to the above, any CIcon control in the window with the same family
  46. **   is redrawn in hilite==0 state, and it's value is turned off.  The CIcon control
  47. **   that was clicked on has it's value immediately set to 1.
  48. ** • Any variant #1 CIcon control when drawn in hilite==0 state draws as if it is in
  49. **   hilite==1 state.  This means that once the control is clicked on, it's stuck in
  50. **   hilite==1 state.
  51. ** • The hilite==255 rules still apply.
  52. ** • To allow for a value of 1 to be stored, the max value for the control has to be
  53. **   at least 1.
  54. ** • A third state for the control is optional.  The third state is for double-clicking
  55. **   the control down.  Many applications want to make a tool either a one-shot use
  56. **   or permanent use tool.  By allowing double-clicking on the CIcon control, we can
  57. **   accomplish this.
  58. ** • The value for a double-clicked CIcon control is 2.  This indicates to the
  59. **   control that it should draw the 'cicn' #base+n+n.
  60. ** • To allow for a value of 2, the max value of the control has to be at least 2.
  61. ** • The max value for the CIcon control is used as the family number.  The family number
  62. **   is used to determine which other CIcon control to turn off when this control is
  63. **   clicked down.  Due to this other usage of the max value, bit-0 of the max value
  64. **   is used to determine if double-clicking is allowed.
  65. **       odd  family number (max value), single-clicking only.
  66. **       even family number (max value), double-clicking feature enabled.
  67. */
  68.  
  69.  
  70.  
  71. /*****************************************************************************/
  72.  
  73.  
  74.  
  75. #ifndef __CICONCONTROL__
  76. #include "CIconControl.h"
  77. #endif
  78.  
  79. #ifndef __CONTROLS__
  80. #include <Controls.h>
  81. #endif
  82.  
  83. #ifndef __DTSLib__
  84. #include "DTS.Lib.h"
  85. #endif
  86.  
  87. #ifndef __ERRORS__
  88. #include <Errors.h>
  89. #endif
  90.  
  91. #ifndef __FONTS__
  92. #include <Fonts.h>
  93. #endif
  94.  
  95. #ifndef __GWLAYERS__
  96. #include "GWLayers.h"
  97. #endif
  98.  
  99. #ifndef __MEMORY__
  100. #include <Memory.h>
  101. #endif
  102.  
  103. #ifndef __PACKAGES__
  104. #include <Packages.h>
  105. #endif
  106.  
  107. #ifndef __RESOURCES__
  108. #include <Resources.h>
  109. #endif
  110.  
  111. #ifndef __STRINGUTILS__
  112. #include "StringUtils.h"
  113. #endif
  114.  
  115. #ifndef __UTILITIES__
  116. #include "Utilities.h"
  117. #endif
  118.  
  119.  
  120.  
  121. /*****************************************************************************/
  122.  
  123.  
  124.  
  125. #ifdef powerc
  126. #pragma options align=mac68k
  127. #endif
  128. typedef struct cdefRsrcJMP {
  129.     long    jsrInst;
  130.     long    moveInst;
  131.     short    jmpInst;
  132.     long    jmpAddress;
  133. } cdefRsrcJMP;
  134. typedef cdefRsrcJMP *cdefRsrcJMPPtr, **cdefRsrcJMPHndl;
  135. #ifdef powerc
  136. #pragma options align=reset
  137. #endif
  138.  
  139. typedef struct CbtnInfo {
  140.     short        resIconID;
  141.     short        hicons, vicons;
  142.     short        variant;
  143. } CbtnInfo;
  144. typedef CbtnInfo *CbtnInfoPtr, **CbtnInfoHndl;
  145.  
  146.  
  147.  
  148. /*****************************************************************************/
  149.  
  150.  
  151.  
  152. short    gCIconCtl = rCIconCtl;
  153.  
  154. extern ControlHandle    gWhichCtlHit;
  155. extern Boolean            gWhichCtlDbl;
  156. extern Boolean            gWhichCtlTracking;
  157.  
  158. static pascal long        CCIconCtl   (short varCode, ControlHandle ctl, short msg, long parm);
  159. static pascal void        CCIconAction(ControlHandle ctl, short part);
  160. static pascal void        NoRect(GrafVerb verb, Rect *r);
  161.  
  162. static cdefRsrcJMPHndl    gCDEF;
  163.  
  164.  
  165.  
  166. /*****************************************************************************/
  167. /*****************************************************************************/
  168. /*****************************************************************************/
  169.  
  170.  
  171.  
  172. #pragma segment Controls
  173. static pascal long    CCIconCtl(short varCode, ControlHandle ctl, short msg, long parm)
  174. {
  175.     Rect                viewRct, r;
  176.     short                resIconID;
  177.     CbtnInfoHndl        info;
  178.     short                hicons, vicons, i, h, v, variant, oldCtlVal;
  179.     short                thisHilite, keepHilite, fnum;
  180.     CIconHandle            icon;
  181.     ControlHandle        cc;
  182.     WindowPtr            ww;
  183.     short                txFont, txSize, txMode;
  184.     Style                txFace;
  185.     QDProcs                qdp;
  186.     QDProcsPtr            oldqdp;
  187.     ControlStyleInfo    cinfo;
  188.     LayerObj            wlayer, blayer;
  189.     Handle                hh;
  190.     Str255                str;
  191.  
  192.     static QDRectUPP    qdrupp;
  193.  
  194.     if (!(info = (CbtnInfoHndl)(*ctl)->contrlRfCon)) return(0);
  195.  
  196.     viewRct    = (*ctl)->contrlRect;
  197.     thisHilite = (*ctl)->contrlHilite;
  198.     variant    = ((*info)->variant & 0xFFF7);
  199.  
  200.     switch (msg) {
  201.  
  202.         case drawCntl:
  203.  
  204.             resIconID = (*info)->resIconID;
  205.             hicons    = (*info)->hicons;
  206.             vicons    = (*info)->vicons;
  207.  
  208.             if (variant & 0x01) {
  209.                 if (thisHilite < 2)
  210.                     thisHilite = (*ctl)->contrlValue;
  211.                 if (thisHilite == 2)
  212.                     thisHilite = 255;
  213.             }
  214.  
  215.             wlayer = nil;
  216.  
  217.             switch (thisHilite) {
  218.                 case 1:
  219.                 case 255:
  220.                     i = (thisHilite == 1) ? (resIconID + vicons * hicons) :
  221.                                             (resIconID + 2 * vicons * hicons);
  222.                     SetResLoad(false);
  223.                     if (!GetResource('cicn', i)) i = resIconID;
  224.                     if (GetResource('cicn', i)) {
  225.                         (*info)->resIconID = i;
  226.                         keepHilite = (*ctl)->contrlHilite;
  227.                         (*ctl)->contrlHilite = 99;
  228.                         CCIconCtl(varCode, ctl, msg, parm);
  229.                         (*ctl)->contrlHilite = keepHilite;
  230.                         (*info)->resIconID = resIconID;
  231.                         if (thisHilite == 1)
  232.                             if (i == resIconID)
  233.                                 InvertRect(&viewRct);
  234.                                     /* If there is no hilite==1 'cicn', invert the base 'cicn'. */
  235.                     }
  236.                     SetResLoad(true);
  237.                     return(0);
  238.                     break;
  239.                 default:
  240.                     if (!(variant & 0x01)) resIconID += (*ctl)->contrlValue;
  241.  
  242.                     SetResLoad(false);
  243.                     hh = GetResource('cicn', resIconID);
  244.                     SetResLoad(true);
  245.                     if (!hh) {
  246.                         EraseRect(&viewRct);
  247.                         FrameRect(&viewRct);
  248.                         break;
  249.                     }
  250.                     for (i = v = 0; v < vicons;) {
  251.                         r.top    = viewRct.top + 64 * v;
  252.                         r.bottom = r.top  + 64;
  253.                         if (++v == vicons)
  254.                             r.bottom = viewRct.bottom;
  255.                         if (variant & 0x02) {
  256.                             NewLayer(&wlayer, nil, nil, (*ctl)->contrlOwner, 0, 0);
  257.                             (*wlayer)->dstRect = viewRct;            /* Minimize the size of the offscreen. */
  258.                             NewLayer(&blayer, wlayer, nil, nil, 0, 0);
  259.                             SetLayerWorld(blayer);
  260.                             EraseRect(&viewRct);
  261.                             InvalLayer(wlayer, viewRct, false);
  262.                         }
  263.                         for (h = 0; h < hicons; ++i) {
  264.                             r.left  = viewRct.left + 64 * h;
  265.                             r.right = r.left + 64;
  266.                             if (++h == hicons)
  267.                                 r.right = viewRct.right;
  268.                             icon = ReadCIcon(resIconID + i);
  269.                             if (icon) {
  270.                                 DrawCIcon(icon, r);
  271.                                 KillCIcon(icon);
  272.                             }
  273.                         }
  274.                     }
  275.                     break;
  276.             }
  277.  
  278.             pcpy(str, (*ctl)->contrlTitle);
  279.             if (str[0]) {
  280.                 GetPort(&ww);
  281.                 txFont = ww->txFont;
  282.                 txFace = ww->txFace;
  283.                 txSize = ww->txSize;
  284.                 if (!((*info)->variant & useWFont)) {
  285.                     TextFont(systemFont);
  286.                     TextFace(normal);
  287.                     TextSize(0);
  288.                 }
  289.                 else {
  290.                     if (GetControlStyle(ctl, &cinfo)) {
  291.                         TextFace(cinfo.fontStyle);
  292.                         fnum = systemFont;
  293.                         if (cinfo.font[0])
  294.                             GetFNum(cinfo.font, &fnum);
  295.                         TextFont(fnum);
  296.                         TextSize(cinfo.fontSize);
  297.                     }
  298.                 }
  299.  
  300.                 oldqdp = ww->grafProcs;
  301.                 if (oldqdp)
  302.                     BlockMove(oldqdp, &qdp, sizeof(QDProcs));
  303.                 else
  304.                     SetStdProcs(&qdp);
  305.  
  306.                 if (!qdrupp) {
  307. #if USES68KINLINES
  308.                     qdrupp = (QDRectUPP)NoRect;
  309. #else
  310.                     qdrupp = NewQDRectProc(NoRect);
  311. #endif
  312.                 }
  313.                 qdp.rectProc = qdrupp;
  314.  
  315.                 ww->grafProcs = &qdp;
  316.                 txMode = ww->txMode;
  317.                 TextMode(srcOr);
  318.  
  319.                 viewRct.top -= (*ctl)->contrlMin;
  320. //                GetFontInfo(&finfo);
  321. //                viewRct.top += (finfo.ascent + finfo.descent) / 2;
  322.                 if (viewRct.top < viewRct.bottom)
  323.                     TETextBox(str + 1, str[0], &viewRct, teCenter);
  324.  
  325.                 ww->grafProcs = oldqdp;
  326.                 TextMode(txMode);
  327.  
  328.                 TextFont(txFont);
  329.                 TextFace(txFace);
  330.                 TextSize(txSize);
  331.             }
  332.  
  333.             if (wlayer) {
  334.                 UpdateLayer(wlayer);
  335.                 ResetLayerWorld(blayer);
  336.                 DisposeThisAndBelowLayers(wlayer);
  337.             }
  338.  
  339.             break;
  340.  
  341.         case testCntl:
  342.  
  343.             oldCtlVal = (*ctl)->contrlValue;
  344.  
  345.             if ((*ctl)->contrlHilite == 255) return(0);
  346.                 /* Control disabled, so no click.  (We probably don't get called in this case. */
  347.  
  348.             if (!(variant & 0x01)) return(PtInRect(*(Point *)&parm, &viewRct));
  349.                 /* Don't make user call WhichControl unless the variant
  350.                 ** is for double-clicking.  (Then it has to be called.) */
  351.  
  352.             if (ctl != gWhichCtlHit) return(0);
  353.                 /* WhichControl already found the control hit.  Unless it is the one
  354.                 ** found by WhichControl, consider it unhit. */
  355.  
  356.             if (gWhichCtlTracking) return(1);
  357.                 /* We already handled it, but the control manager is insistent. */
  358.  
  359.             ww = (*ctl)->contrlOwner;        /* Turn off any other controls in this family. */
  360.             for (cc = nil; (cc = CCIconNext(ww, cc, 1, true)) != nil;) {
  361.                 if (cc != ctl) {
  362.                     if (((*cc)->contrlMax & 0xFFF0) == ((*ctl)->contrlMax & 0xFFF0)) {
  363.                         if ((*cc)->contrlValue) {
  364.                             (*cc)->contrlValue  = 0;
  365.                             (*cc)->contrlHilite = 0;
  366.                             CCIconCtl(varCode, cc, drawCntl, 0);
  367.                         }
  368.                     }
  369.                 }
  370.             }
  371.  
  372.             (*ctl)->contrlValue = 1;
  373.             if (gWhichCtlDbl)                            /* If user double-clicked... */
  374.                 if (!((*ctl)->contrlMax & 0x01))        /* If double-clicking allowed... */
  375.                     (*ctl)->contrlValue = 2;
  376.  
  377.             if (oldCtlVal != (*ctl)->contrlValue)
  378.                 CCIconCtl(varCode, ctl, drawCntl, 0);
  379.  
  380.             gWhichCtlTracking = true;
  381.             return(1);
  382.  
  383.             break;
  384.  
  385.         case calcCRgns:
  386.         case calcCntlRgn:
  387.             if (msg == calcCRgns)
  388.                 parm &= 0x00FFFFFF;
  389.             RectRgn((RgnHandle)parm, &viewRct);
  390.             break;
  391.  
  392.         case initCntl:
  393.             break;
  394.  
  395.         case dispCntl:
  396.             DisposeHandle((Handle)(*ctl)->contrlRfCon);
  397.             break;
  398.  
  399.         case posCntl:
  400.             break;
  401.  
  402.         case thumbCntl:
  403.             break;
  404.  
  405.         case dragCntl:
  406.             break;
  407.  
  408.         case autoTrack:
  409.             break;
  410.     }
  411.  
  412.     return(0);
  413. }
  414.  
  415.  
  416.  
  417. /*****************************************************************************/
  418.  
  419.  
  420.  
  421. #pragma segment Controls
  422. ControlHandle    CCIconNew(WindowPtr window, Rect *r, StringPtr title, Boolean vis, short val,
  423.                           short min, short max, short viewID, short refcon)
  424. {
  425.     WindowPtr        oldPort;
  426.     Rect            viewRct;
  427.     Boolean            err;
  428.     ControlHandle    viewCtl;
  429.     CbtnInfoHndl    info;
  430.     short            hicons, vicons;
  431.  
  432.     static ControlActionUPP    cupp;
  433.     static ControlDefUPP    cdefupp;
  434.  
  435.     GetPort(&oldPort);
  436.     SetPort(window);
  437.  
  438.     viewRct = *r;
  439.     viewCtl = nil;
  440.     info    = nil;
  441.  
  442.     err = false;
  443.  
  444.     if (!gCDEF) {
  445.         gCDEF = (cdefRsrcJMPHndl)GetResource('CDEF', (viewID / 16));
  446.         if (gCDEF) {
  447.             if (!cdefupp) {
  448.                 cdefupp = NewControlDefProc(CCIconCtl);
  449.             }
  450.             (*gCDEF)->jmpAddress = (long)cdefupp;
  451.             if (TrapExists(_HWPriv))
  452.                 FlushInstructionCache();
  453.                     /* Make sure that instruction caches don't kill us. */
  454.         }
  455.         else err = true;
  456.     }
  457.  
  458.     if (!err) {
  459.         info = (CbtnInfoHndl)NewHandleClear(sizeof(CbtnInfo));
  460.         if (info) {
  461.             hicons = (viewRct.right - viewRct.left - 1) / 64 + 1;
  462.             if (hicons < 1) hicons = 1;
  463.             vicons = (viewRct.bottom - viewRct.top - 1) / 64 + 1;
  464.             if (vicons < 1) vicons = 1;
  465.     
  466.             (*info)->resIconID = refcon;
  467.             (*info)->hicons    = hicons;
  468.             (*info)->vicons    = vicons;
  469.             (*info)->variant   = (viewID & 0x0F);
  470.         }
  471.         else err = true;
  472.     }
  473.  
  474.     if (!err) {
  475.         viewCtl = NewControl(window, &viewRct, title, vis, val, min, max, viewID, (long)info);
  476.         if (viewCtl) {
  477.             if (!cupp) cupp = NewControlActionProc(CCIconAction);
  478.             SetControlAction(viewCtl, cupp);
  479.         }
  480.         else
  481.             err = true;
  482.     }
  483.  
  484.     SetPort(oldPort);
  485.  
  486.     if (err) {        /* Oops.  Somebody wasn't happy. */
  487.         if (info)
  488.             DisposeHandle((Handle)info);
  489.                 /* viewCtl exists only if no error, so we just have to possibly clean up info. */
  490.     }
  491.  
  492.     return(viewCtl);
  493. }
  494.  
  495.  
  496.  
  497. /*****************************************************************************/
  498.  
  499.  
  500.  
  501. #pragma segment Controls
  502. ControlHandle    CCIconNext(WindowPtr window, ControlHandle ctl, short dir, Boolean justActive)
  503. {
  504.     ControlHandle    nextCtl, priorCtl;
  505.  
  506.     if (!window) return(nil);
  507.     if (!gCDEF)  return(nil);
  508.  
  509.     if (dir > 0) {
  510.         if (!ctl)
  511.             ctl = ((WindowPeek)window)->controlList;
  512.         else
  513.             ctl = (*ctl)->nextControl;
  514.         while (ctl) {
  515.             if ((!justActive) || ((*ctl)->contrlVis)) {
  516.                 if ((!justActive) || ((*ctl)->contrlHilite != 255)) {
  517.                     if (StripAddress((*ctl)->contrlDefProc) == StripAddress(gCDEF))
  518.                         return(ctl);
  519.                             /* The handle may be locked, which means that the hi-bit
  520.                             ** may be on, thus invalidating the compare.  Dereference the
  521.                             ** handles to get rid of this possibility. */
  522.                 }
  523.             }
  524.             ctl = (*ctl)->nextControl;
  525.         }
  526.         return(ctl);
  527.     }
  528.  
  529.     nextCtl = ((WindowPeek)window)->controlList;
  530.     for (priorCtl = nil; ;nextCtl = (*nextCtl)->nextControl) {
  531.         if ((!nextCtl) || (nextCtl == ctl)) return(priorCtl);
  532.         if ((!justActive) || ((*nextCtl)->contrlVis)) {
  533.             if ((!justActive) || ((*nextCtl)->contrlHilite != 255)) {
  534.                 if (StripAddress((*ctl)->contrlDefProc) == StripAddress(gCDEF))
  535.                     priorCtl = nextCtl;
  536.                         /* The handle may be locked, which means that the hi-bit
  537.                         ** may be on, thus invalidating the compare.  Dereference the
  538.                         ** handles to get rid of this possibility. */
  539.             }
  540.         }
  541.     }
  542. }
  543.  
  544.  
  545.  
  546. /*****************************************************************************/
  547.  
  548.  
  549.  
  550. #pragma segment Controls
  551. Boolean    IsCIconCtl(ControlHandle ctl)
  552. {
  553.     if (ctl)
  554.         if (StripAddress((*ctl)->contrlDefProc) == StripAddress(gCDEF))
  555.             return(true);
  556.                 /* The handle may be locked, which means that the hi-bit
  557.                 ** may be on, thus invalidating the compare.  Dereference the
  558.                 ** handles to get rid of this possibility. */
  559.  
  560.     return(false);
  561. }
  562.  
  563.  
  564.  
  565. /*****************************************************************************/
  566.  
  567.  
  568.  
  569. #pragma segment Controls
  570. static pascal void    CCIconAction(ControlHandle ctl, short part)
  571. {
  572.     static short    lastPart = 0;
  573.  
  574.     if (lastPart != part) {
  575.         lastPart = part;
  576.         CCIconCtl(0, ctl, drawCntl, part);
  577.     }
  578. }
  579.  
  580.  
  581.  
  582. /*****************************************************************************/
  583.  
  584.  
  585.  
  586. #pragma segment Controls
  587. static pascal void    NoRect(GrafVerb verb, Rect *r)
  588. {
  589. #ifndef __MWERKS__ 
  590. #pragma unused (verb, r)
  591. #endif
  592. }
  593.  
  594.  
  595.  
  596.